import { EXTENSION_PATH, findFile, FindFileType, copyFile, mapDirectory, exeCommand, getFrameworkModules, getExtensionConfig, confirmEnding } from '../utils'
import { ModulesConfig, ModulesTree } from './modules';

const FS = require('fs');
const Path = require('path');

const TAG = 'lcc-framework';

interface IOptions {
	confuse: boolean,
    clip: boolean;
	clipMethod: 'auto' | 'custom';
	keepModules: { [key:string]:boolean };
}

interface ITaskOptions extends IBuildTaskOption {
    packages: {
        'lcc-framework': IOptions;
	}
}

export async function onAfterCompressSettings(options: ITaskOptions, result: IBuildResult) {
	let destJs = findFile(result.dest, 'lcc-framework.js', FindFileType.FILE);
	const pkgOptions = options.packages['lcc-framework'];
	if(destJs){
        const VERSION = getExtensionConfig().version;
        const tempPath = Path.join(Editor.Project.path, 'temp', 'lcc-framework');
        if(!FS.existsSync(tempPath)){
            FS.mkdirSync(tempPath, { recursive : true });
        }
		let modules = '';
		let modulesConfig:ModulesConfig = getFrameworkModules();
		if(pkgOptions.clip){
			if(pkgOptions.clipMethod == 'auto'){
				let pathList:string[] = [];
				mapDirectory(Path.join(Editor.Project.path, 'assets'), (path, file, isdir)=>{
					if(!isdir && file != 'lcc-framework.js' && 
						(confirmEnding(path, '.ts') || 
						confirmEnding(path, '.js') || 
						confirmEnding(path, '.scene') || 
						confirmEnding(path, '.prefab'))){
						pathList.push(path);
					}
					return false;
				});
				const autoclipCacheConfigFile = Path.join(tempPath, 'autoclip-cache-config.json');
				let autoclipCacheConfig:{ version:number , filetimes:Record<string,number>, modules:string } = { version:VERSION, filetimes:{}, modules:"" };
				do {
					// 检查缓存
					if(FS.existsSync(autoclipCacheConfigFile)){
						autoclipCacheConfig = JSON.parse(FS.readFileSync(autoclipCacheConfigFile, { encoding : 'utf-8' }));
						if(Object.keys(autoclipCacheConfig.filetimes).length == pathList.length && autoclipCacheConfig.version == VERSION){
							if((()=>{
								for(let _path of pathList){
									let pinfo = FS.statSync(_path);
									if(Math.floor(pinfo.mtimeMs) != autoclipCacheConfig.filetimes[_path]){
										return false;
									}
								}
								return true;
							})()){
								console.log(`${TAG} 使用自动裁剪缓存`);
								modules = autoclipCacheConfig.modules;
								break;
							}
						}
					}

					// 重新构建缓存
					if(FS.existsSync(autoclipCacheConfigFile)){
						FS.unlinkSync(autoclipCacheConfigFile);
					}
					autoclipCacheConfig.version = VERSION;
					autoclipCacheConfig.filetimes = {};
					let autoclipCache = '';
					for(let _path of pathList){
						let pinfo = FS.statSync(_path);
						autoclipCacheConfig.filetimes[_path] = Math.floor(pinfo.mtimeMs);
						autoclipCache += FS.readFileSync(_path, { encoding : 'utf-8' });
					}
					let modulesTree = new ModulesTree(modulesConfig);
					for(let mname in modulesConfig){
						let config = modulesConfig[mname];
						if(config.keywords){
							for(let keyword of config.keywords){
								let reg = new RegExp(`[^a-zA-Z_$]${keyword}[^a-zA-Z_$]`);
								if(reg.test(autoclipCache)){
									console.log(`${TAG} KEYWORD '${keyword}' MODULE '${mname}'`);
									modulesTree.keepModule(mname);
									break;
								}
							}
						}
					}
					autoclipCacheConfig.modules = modulesTree.collectModules().filter((mname:string)=>{ return mname.indexOf("base") != 0; }).join(',');
					modules = autoclipCacheConfig.modules;
					FS.writeFileSync(autoclipCacheConfigFile, JSON.stringify(autoclipCacheConfig));
				}while(false);
			}else{
				let keepModules = pkgOptions.keepModules;
				modules = Object.keys(modulesConfig).filter((mname:string)=>{ 
					let module = modulesConfig[mname];
					let keep = keepModules[mname];
					return mname.indexOf("base") != 0 && (keep != null ? keep : module.keepDefault);
				}).join(',');
			}
		}else{
			modules = Object.keys(modulesConfig).filter((mname:string)=>{ return mname.indexOf("base") != 0; }).join(',');
		}
		
		console.log(`${TAG} 模块:${modules}`);
        
        let debug = pkgOptions.confuse ? false : options.debug;
        const option_debug = debug?'--debug':'';
        const option_modules = modules?`--modules "${modules}"`:'';
        const option_confuse = pkgOptions.confuse?'--confuse':'';

        const frameworkCacheConfigFile = Path.join(tempPath, 'framework-cache-config.json');
        const frameworkCacheFile = Path.join(tempPath, 'framework-cache.js');
        let frameworkCacheConfig:{ version:number ,options:Record<string,string> } = { version:VERSION, options:{} };
        do {
            // 检查缓存
            if(FS.existsSync(frameworkCacheConfigFile) && FS.existsSync(frameworkCacheFile)){
                frameworkCacheConfig = JSON.parse(FS.readFileSync(frameworkCacheConfigFile, { encoding : 'utf-8' }));
                if(frameworkCacheConfig.version == VERSION && 
                    frameworkCacheConfig.options['debug'] == option_debug && 
                    frameworkCacheConfig.options['confuse'] == option_confuse && 
                    frameworkCacheConfig.options['modules'] == option_modules){
                    console.log(`${TAG} 使用框架缓存`);
                    break;
                }
            }

			// 重新编译框架
			if(FS.existsSync(frameworkCacheConfigFile)){
				FS.unlinkSync(frameworkCacheConfigFile);
			}
			if(FS.existsSync(frameworkCacheFile)){
				FS.unlinkSync(frameworkCacheFile);
			}
            let command = `gulp buildPublishFramework --outDir "${tempPath}" --outFile framework-cache.js ${option_debug} ${option_confuse} ${option_modules}`;
            let result = await exeCommand(command, EXTENSION_PATH);
            if(result.error){
                console.error('框架编译失败: ' + result.error);
                break;
            }else{
                // 写入缓存
                frameworkCacheConfig.version = VERSION;
                frameworkCacheConfig.options['debug'] = option_debug;
                frameworkCacheConfig.options['confuse'] = option_confuse;
                frameworkCacheConfig.options['modules'] = option_modules;
                FS.writeFileSync(frameworkCacheConfigFile, JSON.stringify(frameworkCacheConfig));
            }
        }while(false);

        if(FS.existsSync(frameworkCacheFile)){
            copyFile(frameworkCacheFile, destJs);
			console.log(`${TAG} lcc-framework.js 替换成功`);
        }
	}
}
